package org.mmtk.plan.scale.colors;

import org.mmtk.plan.scale.ScaleScanner;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.ObjectReference;

@Uninterruptible
public class SColorsScanner extends ScaleScanner {
	private long removedColors;
	private long coloredDeadColorsDead;
	private long coloredDeadColorsLive;
	private long coloredDeadColorsTraced;
	private long coloredLiveColorsDead;
	private long coloredLiveColorsLost;
	private long coloredLiveColorsLive;
	
	
	@Override
	public void processCell(ObjectReference objRef, int color) {
		ObjectReference colorMark = VM.objectModel.getKathy(objRef);
		switch(scan) {
		case 1: 
		case 2: 
		{
			super.processCell(objRef, color);
			if (colorMark != ObjectReference.nullReference()) {
				VM.objectModel.setKathy(objRef, ObjectReference.nullReference());
				removedColors++;
				return;
			}
			break;
		}
		case 3: 
		{
			scanColors(objRef, color, colorMark, false);
			break;
		}
		case 4: 
		{			
			if (colorMark != ObjectReference.nullReference()) {
				if (Options.scaleDebug.getValue() > 7) {
					Log.write("SColorsScanner.processCell, release colored object objRef=");
					Log.flush();
					VM.objectModel.dumpObject(objRef);
				}
				VM.objectModel.setKathy(objRef, ObjectReference.nullReference());
				removedColors++;
			}
			scanColors(objRef, color, colorMark, true);
			break;
		}
		case 5: 
		{			
			break;
		}
		}
	}
	
	private void scanColors(ObjectReference objRef, int color, ObjectReference colorMark, boolean act) {
		if (Options.scaleDebug.getValue() > 5) {
			Log.write("SColorsScanner.scanColors, objRef=");
			Log.flush();
			VM.objectModel.dumpObject(objRef);
			Log.flush();
		}
		
		if (colorMark != ObjectReference.nullReference()) {
			//colored
			Color addedColor = Color.getColor(colorMark);
			if (addedColor == null) {
				// sanity
				Log.write("SColorsScanner.scanColors !!! object colored bad color, objRef=");
				Log.flush();
				VM.objectModel.dumpObject(objRef);
				Log.flush();
				return;
			}
			if (addedColor.isLive()) {
				if (color == 0) {
					if (Options.scaleDebug.getValue() > 5) {
						Log.writeln("SColorsScanner.scanAfterCheck, dead object colored live color: ");
						Log.flush();
						VM.objectModel.dumpObject(objRef);
						Log.flush();
					}				
					coloredLiveColorsDead++;
					return;
				}
				if (color == prevMarkColor - 1) {
					if (Options.scaleDebug.getValue() > 5) {
						Log.writeln("SColorsScanner.scanColors, object colored live color");
						Log.flush();
						VM.objectModel.dumpObject(objRef);
						Log.flush();
					}
					coloredLiveColorsLive++;
					if(act) {
						this.markedObjects++;
						SColors.scaleSpace.markObject(objRef);
					}
					return;
				}
				if (color == prevMarkColor) {
					if (Options.scaleDebug.getValue() > 1) {
						Log.writeln("SColorsScanner.scanColors, !!! colored live, traced in fix and not in check");
					}
					coloredLiveColorsLost++;
					if(act) {
						//conservatively mark
						this.markedObjects++;
						SColors.scaleSpace.markObject(objRef);
					}
					return;
				}
				//should not get here
				Log.writeln("SColorsScanner.scanColors, object colored live color and marked strange mark color !!! ");
				Log.flush();
				VM.objectModel.dumpObject(objRef);
				Log.flush();
				return;
			}
			//dead color
			if (color == 0) {
				coloredDeadColorsDead++;
				return;
			}
			if (color == prevMarkColor - 1) {
				if (Options.scaleDebug.getValue() > 1) {
					Log.writeln("SColorsScanner.scanColors, !!! object colored dead color and traced");
					Log.flush();
					VM.objectModel.dumpObject(objRef);
					Log.flush();
				}
				coloredDeadColorsTraced++;
				if(act) {
					this.unmarkedObjects++;
					SColors.scaleSpace.writeAllocState(objRef);
				}
				return;
			}
			if (color == prevMarkColor) {
				if (Options.scaleDebug.getValue() > 1) {
					Log.writeln("SColorsScanner.scanColors, !!! object colored dead color and found by check");
					Log.flush();
					VM.objectModel.dumpObject(objRef);
					Log.flush();
				}
				coloredDeadColorsLive++;
				if(act) {
					//conservatively mark
					this.markedObjects++;
					SColors.scaleSpace.markObject(objRef);
				}
				return;
			}
			//should not get here
			Log.writeln("SColorsScanner.scanColors, object colored dead color and marked strange mark color !!! ");
			Log.flush();
			VM.objectModel.dumpObject(objRef);
			Log.flush();
			return;
		}
		//not colored
		if (color == prevMarkColor - 1) {
			if (Options.scaleDebug.getValue() > 1) {
				Log.writeln("SColorsScanner.scanColors, !!! not colored, live not traced");
				Log.flush();
				VM.objectModel.dumpObject(objRef);
				Log.flush();
			}
			return;
		}
		if (color == prevMarkColor && act ) {
			if (Options.scaleDebug.getValue() > 4) {
				Log.write("SColorsScanner.scanColors, !!! not colored object traced in fix but not in check ");
				Log.flush();
				VM.objectModel.dumpObject(objRef);
				Log.flush();
			}
			coloredLiveColorsLost++;
			//conservatively mark
			this.markedObjects++;
			SColors.scaleSpace.markObject(objRef);
			return;
		}
		
		return;
	}
				
	@Override
	public void start(int prevMarkColor, int scan) {
		super.start(prevMarkColor, scan);
		removedColors = 0;
		coloredLiveColorsDead = 0;
		coloredLiveColorsLost = 0;
		coloredLiveColorsLive = 0;
		coloredDeadColorsDead = 0;
		coloredDeadColorsLive = 0;
		coloredDeadColorsTraced = 0;
		this.markedObjects = 0;
		this.unmarkedObjects = 0;
	}

	@Override
	public void end() {
		super.end();
		Log.write("Color table state: ");
		Log.flush();
		VM.objectModel.dumpObject(Color.tableRef);
		if(scan==1 || scan ==2) {
			Log.write("SColorsScanner.summary, removedColors=");
			Log.writeln(removedColors);
			return;
		}
		if(scan==3 || scan==4) {
			Log.write("SColorsScanner.end, coloredLiveColorsLive=");
			Log.write(coloredLiveColorsLive);
			Log.write(", coloredLiveColorsDead=");
			Log.write(coloredLiveColorsDead);
			Log.write(", coloredLiveColorsTraced=");
			Log.write(coloredLiveColorsLost);	
			Log.write(", coloredDeadColorsLive=");
			Log.write(coloredDeadColorsLive);
			Log.write(", coloredDeadColorsDead=");
			Log.write(coloredDeadColorsDead);
			Log.write(", coloredDeadColorsTraced=");
			Log.writeln(coloredDeadColorsTraced);	
			
		}
		if(scan==4) {
			Log.write("SColorsScanner.end, removedColors=");
			Log.writeln(removedColors);
		}
	}
}
